local FFIUtil = require("ffi/util")
local Font = require("ui/font")
local FontList = require("fontlist")
local InfoMessage = require("ui/widget/infomessage")
local UIManager = require("ui/uimanager")
local util = require("util")
local _ = require("gettext")
local T = FFIUtil.template

-- Some "Noto Sans *" fonts are already in ui/font.lua Font.fallbacks,
-- because they are required to display supported UI languages.
-- Don't allow them to be disabled.
-- Duplicated from (and should be kept in sync with) ui/font.lua, but here
-- by face name and there by font file/path name.
local hardcoded_fallbacks = {
    "Noto Sans CJK SC",
    "Noto Sans Arabic UI",
    "Noto Sans Devanagari UI",
    "Noto Sans Bengali UI",
}
-- Add any user font after Noto Sans CJK SC in the menu
local additional_fallback_insert_indice = 2 -- (indice in the above list)

local alt_name = {
    -- Make it clear this "CJK SC" font actually supports all other CJK variant
    ["Noto Sans CJK SC"] = "Noto Sans CJK SC (TC, JA, KO)"
}

local fallback_candidates = nil
local fallback_candidates_path_to_name = nil

local genFallbackCandidates = function()
    if fallback_candidates then
        return
    end
    fallback_candidates = {}
    fallback_candidates_path_to_name = {}
    for _, font_path in ipairs(FontList:getFontList()) do
        local fontinfo = FontList.fontinfo[font_path] -- (NotoColorEmoji.tff happens to get no fontinfo)
        if fontinfo and #fontinfo == 1 then -- Ignore font files with multiple faces
            fontinfo = fontinfo[1]
            if util.stringStartsWith(fontinfo.name, "Noto Sans ") and
                        not fontinfo.bold and not fontinfo.italic and
                        not fontinfo.serif and not fontinfo.mono then
                fallback_candidates[fontinfo.name] = fontinfo
                fallback_candidates_path_to_name[font_path] = fontinfo.name
            end
        end
    end
end

local more_info_text = _([[
If some book titles, dictionary entries and such are not displayed well but shown as ￾￾ or ��, it may be necessary to download the required fonts for those languages. They can then be enabled as additional UI fallback fonts.
Fonts for many languages can be downloaded at:

https://fonts.google.com/noto

Only fonts named "Noto Sans xyz" or "Noto Sans xyz UI" (regular, not bold nor italic, not Serif) will be available in this menu.]])

local getSubMenuItems = function()
    genFallbackCandidates()
    -- Order the menu items in the order the fallback fonts are used
    local seen_names = {}
    local ordered_names = {}
    local checked_names = {}
    local enabled_names = {}
    for _, name in ipairs(hardcoded_fallbacks) do
        table.insert(ordered_names, name)
        seen_names[name] = true
        checked_names[name] = true
        enabled_names[name] = false
    end
    if G_reader_settings:has("font_ui_fallbacks") then
        local additional_fallbacks = G_reader_settings:readSetting("font_ui_fallbacks")
        for i=#additional_fallbacks, 1, -1 do
            local path = additional_fallbacks[i]
            local name = fallback_candidates_path_to_name[path]
            if not name or seen_names[name] then
                -- No longer found, or made hardcoded: remove it
                table.remove(additional_fallbacks, i)
            else
                table.insert(ordered_names, additional_fallback_insert_indice, name)
                seen_names[name] = true
                checked_names[name] = true
                enabled_names[name] = true
            end
        end
        if #additional_fallbacks == 0 then -- all removed
            G_reader_settings:delSetting("font_ui_fallbacks")
        end
    end
    local add_separator_idx = #ordered_names
    for name, fontinfo in FFIUtil.orderedPairs(fallback_candidates) do
        if not seen_names[name] then
            table.insert(ordered_names, name)
            checked_names[name] = false
            enabled_names[name] = true
        end
    end

    local menu_items = {
        {
            text = _("About additional UI fallback fonts"),
            callback = function()
                UIManager:show(InfoMessage:new{
                    text = more_info_text,
                })
            end,
            keep_menu_open = true,
            separator = true,
        }
    }
    for idx, name in ipairs(ordered_names) do
        local fontinfo = fallback_candidates[name]
        local item = {
            text = alt_name[name] or name,
            separator = idx == add_separator_idx,
            checked_func = function()
                return checked_names[name]
            end,
            enabled_func = function()
                return enabled_names[name]
            end,
            callback = function()
                local additional_fallbacks = G_reader_settings:readSetting("font_ui_fallbacks", {})
                if checked_names[name] then -- enabled: remove it
                    for i=#additional_fallbacks, 1, -1 do
                        if additional_fallbacks[i] == fontinfo.path then
                            table.remove(additional_fallbacks, i)
                            if #additional_fallbacks == 0 then
                                G_reader_settings:delSetting("font_ui_fallbacks")
                            end
                            break
                        end
                    end
                    checked_names[name] = false
                else -- disabled: add it
                    if #additional_fallbacks < Font.additional_fallback_max_nb then
                        table.insert(additional_fallbacks, 1, fontinfo.path)
                        checked_names[name] = true
                    else
                        UIManager:show(InfoMessage:new{
                            text = T(_("The number of allowed additional fallback fonts is limited to %1.\nUncheck some of them if you want to add this one."), Font.additional_fallback_max_nb),
                        })
                        return
                    end
                end
                UIManager:show(InfoMessage:new{
                    text = _("This will take effect on next restart."),
                })
            end,
        }
        table.insert(menu_items, item)
    end
    return menu_items
end

return {
    text = _("Additional UI fallback fonts"),
    sub_item_table_func = getSubMenuItems,
}
